package movee.io.dunet;


import movee.domain.exception.AppException;
import movee.domain.exception.AppStatusCode;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 *
 *
 * @author movee
 */
@Slf4j
@Service
public class DunetClient {

    private static final int DUNET_MAX_PAGE_SIZE = 200;
    private static final int DUNET_MAX_RETRY_CNT = 10;
    private static final Type DUNET_DEVICE_TYPE = new TypeToken<DunetMsg<DunetDeviceDto>>() { }.getType();

    private final Gson gson = new GsonBuilder().serializeNulls().create();
    private final DunetFeign dunetFeign;

    @Autowired
    public DunetClient(DunetFeign dunetFeign) {
        this.dunetFeign = dunetFeign;
    }

    /**
     * 获取设备表所有条目
     * @param query 查询条件
     * @return
     */
    public List<DunetDeviceDto> getDevices(DunetDeviceQuery query) {

        try {
            Map<String, Object> queryMap = dunetDeviceQueryAsMap(query);
            log.debug("queryMap: {}", gson.toJson(queryMap));
            String responseBody = dunetFeign.getDevices(queryMap);
            if (!StringUtils.isEmpty(responseBody)) {
                JsonObject response = (JsonObject) JsonParser.parseString(responseBody);

                if (response.has("status")
                        && response.getAsJsonPrimitive("status").getAsInt() == 0) {

                    DunetMsg<DunetDeviceDto> msg = gson.fromJson(responseBody, DUNET_DEVICE_TYPE);
                    if (msg != null && msg.getMessage() != null && msg.getMessage().getData() != null) {
                        return msg.getMessage().getData();
                    }
                } else {
                    log.warn("get dunet device failed, pageNo: {}, pageSize: {}, response: {}",
                            query.getPage(), query.getSize(), responseBody);
                }
            }
        } catch (Throwable t) {
            log.error("getDevices failed. query: {}, stack: {}", gson.toJson(query), ExceptionUtils.getStackTrace(t));
            throw new AppException(AppStatusCode.ABNORMAL_DEPENDENCY_SERVICE,
                    "（第三方外部接口）权威字典查询设备信息接口返回失败", t);
        }

        return new ArrayList<>();
    }

    public List<DunetDeviceDto> getTotalDevices(DunetDeviceQuery query) {
        List<DunetDeviceDto> devices = new ArrayList<>();

        int page = 1;
        int dtoSize = 0;
        int retryCount = 0;
        do {
            try {
                if (retryCount >= DUNET_MAX_RETRY_CNT) {
                    // 重试超过10次直接放弃
                    log.error("getTotalDevices failed, page: {}, retryCount: {}", page, retryCount);
                    throw new AppException(AppStatusCode.ABNORMAL_DEPENDENCY_SERVICE,
                            "（第三方外部接口）权威字典查询全量设备信息接口失败");
                }

                query.setPage(page);
                query.setSize(DUNET_MAX_PAGE_SIZE);
                List<DunetDeviceDto> dtos = getDevices(query);
                devices.addAll(dtos);

                page++;
                dtoSize = dtos.size();
                retryCount = 0;
            } catch (Exception e) {
                retryCount++;
                log.warn("getTotalDevices failed, page: {}, retryCount: {}, {}",
                        page, retryCount, ExceptionUtils.getStackTrace(e));
            }

        } while (dtoSize >= DUNET_MAX_PAGE_SIZE);

        return devices;
    }

    private Map<String, Object> dunetDeviceQueryAsMap(DunetDeviceQuery query) {

        Map<String, Object> map = gson.fromJson(gson.toJson(query), new TypeToken<Map<String, Object>>() { }.getType());
        Map<String, Object> result = new HashMap<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != null) {
                if ("page_no".equals(entry.getKey()) || "page_size".equals(entry.getKey())) {
                    result.put(entry.getKey(), Integer.valueOf(((Double) entry.getValue()).intValue()));
                } else {
                    result.put(entry.getKey(), entry.getValue());
                }
            }
        }
        return result;
    }


}
